package com.cmcc.shared.network
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.util.ArrayList
import java.util.Arrays

import javax.net.ssl.KeyManager
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager

/**
 * Created by kelvinsun on 11/3/2017.
 */

class SSLSocketFactoryExtended : SSLSocketFactory {
    private var mSSLContext: SSLContext? = null
    private var mCiphers: Array<String>? = null
    private var mProtocols: Array<String>? = null


    @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
    constructor() {
        initSSLSocketFactoryEx(null, null, null)
    }

    @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
    constructor(tm: Array<TrustManager>, random: SecureRandom) {
        initSSLSocketFactoryEx(null, tm, random)
    }

    override fun getDefaultCipherSuites(): Array<String>? {
        return mCiphers
    }

    override fun getSupportedCipherSuites(): Array<String>? {
        return mCiphers
    }

    @Throws(IOException::class)
    override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
        val factory = mSSLContext!!.socketFactory
        val ss = factory.createSocket(s, host, port, autoClose) as SSLSocket

        ss.enabledProtocols = mProtocols
        ss.enabledCipherSuites = mCiphers

        return ss
    }

    @Throws(IOException::class)
    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
        val factory = mSSLContext!!.socketFactory
        val ss = factory.createSocket(address, port, localAddress, localPort) as SSLSocket

        ss.enabledProtocols = mProtocols
        ss.enabledCipherSuites = mCiphers

        return ss
    }

    @Throws(IOException::class)
    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
        val factory = mSSLContext!!.socketFactory
        val ss = factory.createSocket(host, port, localHost, localPort) as SSLSocket

        ss.enabledProtocols = mProtocols
        ss.enabledCipherSuites = mCiphers

        return ss
    }

    @Throws(IOException::class)
    override fun createSocket(host: InetAddress, port: Int): Socket {
        val factory = mSSLContext!!.socketFactory
        val ss = factory.createSocket(host, port) as SSLSocket

        ss.enabledProtocols = mProtocols
        ss.enabledCipherSuites = mCiphers

        return ss
    }

    @Throws(IOException::class)
    override fun createSocket(host: String, port: Int): Socket {
        val factory = mSSLContext!!.socketFactory
        val ss = factory.createSocket(host, port) as SSLSocket

        ss.enabledProtocols = mProtocols
        ss.enabledCipherSuites = mCiphers

        return ss
    }

    @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
    private fun initSSLSocketFactoryEx(km: Array<KeyManager>?, tm: Array<TrustManager>?, random: SecureRandom?) {
        mSSLContext = SSLContext.getInstance("TLS")
        mSSLContext!!.init(km, tm, random)

        mProtocols = GetProtocolList()
        mCiphers = GetCipherList()
    }

    protected fun GetProtocolList(): Array<String> {
        val protocols = arrayOf("TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3")
        var availableProtocols: Array<String>

        var socket: SSLSocket? = null

        try {
            val factory = mSSLContext!!.socketFactory
            socket = factory.createSocket() as SSLSocket

            availableProtocols = socket.supportedProtocols
        } catch (e: Exception) {
            return arrayOf("TLSv1")
        } finally {
            if (socket != null)
                try {
                    socket.close()
                } catch (e: IOException) {
                }

        }

        val resultList = ArrayList<String>()
        for (i in protocols.indices) {
            val idx = Arrays.binarySearch(availableProtocols, protocols[i])
            if (idx >= 0)
                resultList.add(protocols[i])
        }

        return resultList.toTypedArray()
    }

    protected fun GetCipherList(): Array<String> {
        val factory = mSSLContext!!.socketFactory
        val resultList = ArrayList(listOf(*factory.supportedCipherSuites))
        return resultList.toTypedArray()
    }

}